@@ -147,7 +147,7 @@ ENABLE_SECOND_PRECISION_SCHEDULE=false |
||
147 | 147 |
# Specify the scheduler frequency in seconds (default: 0.3). |
148 | 148 |
# Increasing this value will help reduce the use of system resources |
149 | 149 |
# at the expense of time accuracy. |
150 |
-#SCHEDULER_FREQUENCY=0.3 |
|
150 |
+SCHEDULER_FREQUENCY=0.3 |
|
151 | 151 |
|
152 | 152 |
# Use Graphviz for generating diagrams instead of using Google Chart |
153 | 153 |
# Tools. Specify a dot(1) command path built with SVG support |
@@ -159,3 +159,9 @@ TIMEZONE="Pacific Time (US & Canada)" |
||
159 | 159 |
|
160 | 160 |
# Number of failed jobs to keep in the database |
161 | 161 |
FAILED_JOBS_TO_KEEP=100 |
162 |
+ |
|
163 |
+# Maximum runtime of background jobs in minutes |
|
164 |
+DELAYED_JOB_MAX_RUNTIME=20 |
|
165 |
+ |
|
166 |
+# Amount of seconds for delayed_job to sleep before checking for new jobs |
|
167 |
+DELAYED_JOB_SLEEP_DELAY=10 |
@@ -58,6 +58,7 @@ gem 'daemons', '~> 1.1.9' |
||
58 | 58 |
gem 'delayed_job', '~> 4.0.0' |
59 | 59 |
gem 'delayed_job_active_record', '~> 4.0.0' |
60 | 60 |
gem 'devise', '~> 3.4.0' |
61 |
+gem 'dotenv-rails', '~> 2.0.1' |
|
61 | 62 |
gem 'em-http-request', '~> 1.1.2' |
62 | 63 |
gem 'faraday', '~> 0.9.0' |
63 | 64 |
gem 'faraday_middleware' |
@@ -96,27 +97,25 @@ group :development do |
||
96 | 97 |
gem 'guard' |
97 | 98 |
gem 'guard-livereload' |
98 | 99 |
gem 'guard-rspec' |
99 |
-end |
|
100 | 100 |
|
101 |
-group :development, :test do |
|
102 |
- gem 'coveralls', require: false |
|
103 |
- gem 'delorean' |
|
104 |
- gem 'dotenv-rails' |
|
105 |
- gem 'pry' |
|
106 |
- gem 'rr' |
|
107 |
- gem 'rspec', '~> 3.2' |
|
108 |
- gem 'rspec-collection_matchers', '~> 1.1.0' |
|
109 |
- gem 'rspec-rails', '~> 3.1' |
|
110 |
- gem 'rspec-html-matchers', '~> 0.7' |
|
111 |
- gem 'shoulda-matchers' |
|
112 |
- gem 'spring', '~> 1.3.0' |
|
113 |
- gem 'spring-commands-rspec' |
|
114 |
- gem 'vcr' |
|
115 |
- gem 'webmock', '~> 1.17.4', require: false |
|
101 |
+ group :test do |
|
102 |
+ gem 'coveralls', require: false |
|
103 |
+ gem 'delorean' |
|
104 |
+ gem 'pry' |
|
105 |
+ gem 'rr' |
|
106 |
+ gem 'rspec', '~> 3.2' |
|
107 |
+ gem 'rspec-collection_matchers', '~> 1.1.0' |
|
108 |
+ gem 'rspec-rails', '~> 3.1' |
|
109 |
+ gem 'rspec-html-matchers', '~> 0.7' |
|
110 |
+ gem 'shoulda-matchers' |
|
111 |
+ gem 'spring', '~> 1.3.0' |
|
112 |
+ gem 'spring-commands-rspec' |
|
113 |
+ gem 'vcr' |
|
114 |
+ gem 'webmock', '~> 1.17.4', require: false |
|
115 |
+ end |
|
116 | 116 |
end |
117 | 117 |
|
118 | 118 |
group :production do |
119 |
- gem 'dotenv-deployment' |
|
120 | 119 |
gem 'rack' |
121 | 120 |
end |
122 | 121 |
|
@@ -126,15 +125,23 @@ gem 'tzinfo', '>= 1.2.0' # required by rails; 1.2.0 has support for *BSD and Sol |
||
126 | 125 |
# Windows does not have zoneinfo files, so bundle the tzinfo-data gem. |
127 | 126 |
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw] |
128 | 127 |
|
129 |
-# This hack needs some explanation. When on Heroku, use the pg, unicorn, and rails12factor gems. |
|
130 |
-# When not on Heroku, we still want our Gemfile.lock to include these gems, so we scope them to |
|
131 |
-# an unsupported platform. |
|
132 |
-if ENV['ON_HEROKU'] || ENV['HEROKU_POSTGRESQL_ROSE_URL'] || ENV['HEROKU_POSTGRESQL_GOLD_URL'] || File.read(File.join(File.dirname(__FILE__), 'Procfile')) =~ /intended for Heroku/ |
|
128 |
+# Introduces a scope for Heroku specific gems. |
|
129 |
+def on_heroku |
|
130 |
+ if ENV['ON_HEROKU'] || |
|
131 |
+ ENV['HEROKU_POSTGRESQL_ROSE_URL'] || |
|
132 |
+ ENV['HEROKU_POSTGRESQL_GOLD_URL'] || |
|
133 |
+ File.read(File.join(File.dirname(__FILE__), 'Procfile')) =~ /intended for Heroku/ |
|
134 |
+ yield |
|
135 |
+ else |
|
136 |
+ # When not on Heroku, we still want our Gemfile.lock to include |
|
137 |
+ # Heroku specific gems, so we scope them to an unsupported |
|
138 |
+ # platform. |
|
139 |
+ platform :ruby_18, &proc |
|
140 |
+ end |
|
141 |
+end |
|
142 |
+ |
|
143 |
+on_heroku do |
|
133 | 144 |
gem 'pg' |
134 | 145 |
gem 'unicorn' |
135 | 146 |
gem 'rails_12factor', group: :production |
136 |
-else |
|
137 |
- gem 'pg', platform: :ruby_18 |
|
138 |
- gem 'unicorn', platform: :ruby_18 |
|
139 |
- gem 'rails_12factor', platform: :ruby_18 |
|
140 | 147 |
end |
@@ -124,11 +124,9 @@ GEM |
||
124 | 124 |
docile (1.1.5) |
125 | 125 |
domain_name (0.5.24) |
126 | 126 |
unf (>= 0.0.5, < 1.0.0) |
127 |
- dotenv (0.11.1) |
|
128 |
- dotenv-deployment (~> 0.0.2) |
|
129 |
- dotenv-deployment (0.0.2) |
|
130 |
- dotenv-rails (0.11.1) |
|
131 |
- dotenv (= 0.11.1) |
|
127 |
+ dotenv (2.0.1) |
|
128 |
+ dotenv-rails (2.0.1) |
|
129 |
+ dotenv (= 2.0.1) |
|
132 | 130 |
dropbox-api (0.4.2) |
133 | 131 |
hashie |
134 | 132 |
multi_json |
@@ -507,8 +505,7 @@ DEPENDENCIES |
||
507 | 505 |
delayed_job_active_record (~> 4.0.0) |
508 | 506 |
delorean |
509 | 507 |
devise (~> 3.4.0) |
510 |
- dotenv-deployment |
|
511 |
- dotenv-rails |
|
508 |
+ dotenv-rails (~> 2.0.1) |
|
512 | 509 |
dropbox-api |
513 | 510 |
em-http-request (~> 1.1.2) |
514 | 511 |
faraday (~> 0.9.0) |
@@ -33,3 +33,29 @@ class @Utils |
||
33 | 33 |
onHide?() |
34 | 34 |
body?(modal.querySelector('.modal-body')) |
35 | 35 |
$(modal).modal('show') |
36 |
+ |
|
37 |
+ @handleDryRunButton: (button, data = $(button.form).serialize()) -> |
|
38 |
+ $(button).prop('disabled', true) |
|
39 |
+ $('body').css(cursor: 'progress') |
|
40 |
+ $.ajax type: 'POST', url: $(button).data('action-url'), dataType: 'json', data: data |
|
41 |
+ .always => |
|
42 |
+ $('body').css(cursor: 'auto') |
|
43 |
+ .done (json) => |
|
44 |
+ Utils.showDynamicModal """ |
|
45 |
+ <h5>Log</h5> |
|
46 |
+ <pre class="agent-dry-run-log"></pre> |
|
47 |
+ <h5>Events</h5> |
|
48 |
+ <pre class="agent-dry-run-events"></pre> |
|
49 |
+ <h5>Memory</h5> |
|
50 |
+ <pre class="agent-dry-run-memory"></pre> |
|
51 |
+ """, |
|
52 |
+ body: (body) -> |
|
53 |
+ $(body). |
|
54 |
+ find('.agent-dry-run-log').text(json.log).end(). |
|
55 |
+ find('.agent-dry-run-events').text(json.events).end(). |
|
56 |
+ find('.agent-dry-run-memory').text(json.memory) |
|
57 |
+ title: 'Dry Run Results', |
|
58 |
+ onHide: -> $(button).prop('disabled', false) |
|
59 |
+ .fail (xhr, status, error) -> |
|
60 |
+ alert('Error: ' + error) |
|
61 |
+ $(button).prop('disabled', false) |
@@ -174,32 +174,7 @@ class @AgentEditPage |
||
174 | 174 |
|
175 | 175 |
invokeDryRun: (e) => |
176 | 176 |
e.preventDefault() |
177 |
- button = e.target |
|
178 |
- $(button).prop('disabled', true) |
|
179 |
- $('body').css(cursor: 'progress') |
|
180 |
- @updateFromEditors() |
|
181 |
- $.ajax type: 'POST', url: $(button).data('action-url'), dataType: 'json', data: $(button.form).serialize() |
|
182 |
- .always => |
|
183 |
- $("body").css(cursor: 'auto') |
|
184 |
- .done (json) => |
|
185 |
- Utils.showDynamicModal """ |
|
186 |
- <h5>Log</h5> |
|
187 |
- <pre class="agent-dry-run-log"></pre> |
|
188 |
- <h5>Events</h5> |
|
189 |
- <pre class="agent-dry-run-events"></pre> |
|
190 |
- <h5>Memory</h5> |
|
191 |
- <pre class="agent-dry-run-memory"></pre> |
|
192 |
- """, |
|
193 |
- body: (body) -> |
|
194 |
- $(body). |
|
195 |
- find('.agent-dry-run-log').text(json.log).end(). |
|
196 |
- find('.agent-dry-run-events').text(json.events).end(). |
|
197 |
- find('.agent-dry-run-memory').text(json.memory) |
|
198 |
- title: 'Dry Run Results', |
|
199 |
- onHide: -> $(button).prop('disabled', false) |
|
200 |
- .fail (xhr, status, error) -> |
|
201 |
- alert('Error: ' + error) |
|
202 |
- $(button).prop('disabled', false) |
|
177 |
+ Utils.handleDryRunButton(this) |
|
203 | 178 |
|
204 | 179 |
$ -> |
205 | 180 |
Utils.registerPage(AgentEditPage, forPathsMatching: /^agents/) |
@@ -8,13 +8,9 @@ |
||
8 | 8 |
} |
9 | 9 |
|
10 | 10 |
.overlay-container { |
11 |
- position: absolute; |
|
12 |
- top: 0; |
|
13 |
- left: 0; |
|
14 | 11 |
z-index: auto; |
15 | 12 |
|
16 | 13 |
.overlay { |
17 |
- position: relative; |
|
18 | 14 |
z-index: auto; |
19 | 15 |
width: 100%; |
20 | 16 |
height: 100%; |
@@ -25,6 +25,10 @@ module DryRunnable |
||
25 | 25 |
) |
26 | 26 |
end |
27 | 27 |
|
28 |
+ def dry_run? |
|
29 |
+ is_a? Sandbox |
|
30 |
+ end |
|
31 |
+ |
|
28 | 32 |
module Sandbox |
29 | 33 |
attr_accessor :results |
30 | 34 |
|
@@ -35,15 +35,18 @@ class AgentsController < ApplicationController |
||
35 | 35 |
end |
36 | 36 |
|
37 | 37 |
def dry_run |
38 |
- attrs = params[:agent] |
|
38 |
+ attrs = params[:agent] || {} |
|
39 | 39 |
if agent = current_user.agents.find_by(id: params[:id]) |
40 | 40 |
# PUT /agents/:id/dry_run |
41 |
- type = agent.type |
|
41 |
+ if attrs.present? |
|
42 |
+ type = agent.type |
|
43 |
+ agent = Agent.build_for_type(type, current_user, attrs) |
|
44 |
+ end |
|
42 | 45 |
else |
43 | 46 |
# POST /agents/dry_run |
44 | 47 |
type = attrs.delete(:type) |
48 |
+ agent = Agent.build_for_type(type, current_user, attrs) |
|
45 | 49 |
end |
46 |
- agent = Agent.build_for_type(type, current_user, attrs) |
|
47 | 50 |
agent.name ||= '(Untitled)' |
48 | 51 |
|
49 | 52 |
if agent.valid? |
@@ -1,9 +1,13 @@ |
||
1 | 1 |
class DiagramsController < ApplicationController |
2 | 2 |
def show |
3 |
- @agents = if params[:scenario_id].present? |
|
4 |
- current_user.scenarios.find(params[:scenario_id]).agents.includes(:receivers) |
|
5 |
- else |
|
6 |
- current_user.agents.includes(:receivers) |
|
7 |
- end |
|
3 |
+ if params[:scenario_id].present? |
|
4 |
+ @scenario = current_user.scenarios.find(params[:scenario_id]) |
|
5 |
+ agents = @scenario.agents |
|
6 |
+ else |
|
7 |
+ agents = current_user.agents |
|
8 |
+ end |
|
9 |
+ @disabled_agents = agents.inactive |
|
10 |
+ agents = agents.active if params[:exclude_disabled].present? |
|
11 |
+ @agents = agents.includes(:receivers) |
|
8 | 12 |
end |
9 | 13 |
end |
@@ -60,7 +60,8 @@ class Agent < ActiveRecord::Base |
||
60 | 60 |
has_many :scenario_memberships, :dependent => :destroy, :inverse_of => :agent |
61 | 61 |
has_many :scenarios, :through => :scenario_memberships, :inverse_of => :agents |
62 | 62 |
|
63 |
- scope :active, -> { where(disabled: false) } |
|
63 |
+ scope :active, -> { where(disabled: false) } |
|
64 |
+ scope :inactive, -> { where(disabled: true) } |
|
64 | 65 |
|
65 | 66 |
scope :of_type, lambda { |type| |
66 | 67 |
type = case type |
@@ -6,13 +6,15 @@ module Agents |
||
6 | 6 |
class ImapFolderAgent < Agent |
7 | 7 |
cannot_receive_events! |
8 | 8 |
|
9 |
+ can_dry_run! |
|
10 |
+ |
|
9 | 11 |
default_schedule "every_30m" |
10 | 12 |
|
11 | 13 |
description <<-MD |
12 | 14 |
|
13 | 15 |
The ImapFolderAgent checks an IMAP server in specified folders |
14 | 16 |
and creates Events based on new mails found since the last run. |
15 |
- In the first visit to a foler, this agent only checks for the |
|
17 |
+ In the first visit to a folder, this agent only checks for the |
|
16 | 18 |
initial status and does not create events. |
17 | 19 |
|
18 | 20 |
Specify an IMAP server to connect with `host`, and set `ssl` to |
@@ -45,8 +47,8 @@ module Agents |
||
45 | 47 |
specified, will be chosen as the "body" value in a created |
46 | 48 |
event. |
47 | 49 |
|
48 |
- Named captues will appear in the "matches" hash in a created |
|
49 |
- event. |
|
50 |
+ Named captures will appear in the "matches" hash in a |
|
51 |
+ created event. |
|
50 | 52 |
|
51 | 53 |
- "from", "to", "cc" |
52 | 54 |
|
@@ -311,7 +313,7 @@ module Agents |
||
311 | 313 |
|
312 | 314 |
if boolify(interpolated['mark_as_read']) |
313 | 315 |
log 'Marking as read' |
314 |
- mail.mark_as_read |
|
316 |
+ mail.mark_as_read unless dry_run? |
|
315 | 317 |
end |
316 | 318 |
} |
317 | 319 |
end |
@@ -322,7 +324,7 @@ module Agents |
||
322 | 324 |
port = (Integer(port) if port.present?) |
323 | 325 |
|
324 | 326 |
log "Connecting to #{host}#{':%d' % port if port}#{' via SSL' if ssl}" |
325 |
- Client.open(host, port, ssl) { |imap| |
|
327 |
+ Client.open(host, port: port, ssl: ssl) { |imap| |
|
326 | 328 |
log "Logging in as #{username}" |
327 | 329 |
imap.login(username, interpolated[:password]) |
328 | 330 |
|
@@ -437,8 +439,8 @@ module Agents |
||
437 | 439 |
|
438 | 440 |
class Client < ::Net::IMAP |
439 | 441 |
class << self |
440 |
- def open(host, port, ssl) |
|
441 |
- imap = new(host, port, ssl) |
|
442 |
+ def open(host, *args) |
|
443 |
+ imap = new(host, *args) |
|
442 | 444 |
yield imap |
443 | 445 |
ensure |
444 | 446 |
imap.disconnect unless imap.nil? |
@@ -525,17 +527,19 @@ module Agents |
||
525 | 527 |
|
526 | 528 |
def has_attachment? |
527 | 529 |
@has_attachment ||= |
528 |
- begin |
|
529 |
- data = @client.uid_fetch(@uid, 'BODYSTRUCTURE').first |
|
530 |
+ if data = @client.uid_fetch(@uid, 'BODYSTRUCTURE').first |
|
530 | 531 |
struct_has_attachment?(data.attr['BODYSTRUCTURE']) |
532 |
+ else |
|
533 |
+ false |
|
531 | 534 |
end |
532 | 535 |
end |
533 | 536 |
|
534 | 537 |
def fetch |
535 | 538 |
@parsed ||= |
536 |
- begin |
|
537 |
- data = @client.uid_fetch(@uid, 'BODY.PEEK[]').first |
|
539 |
+ if data = @client.uid_fetch(@uid, 'BODY.PEEK[]').first |
|
538 | 540 |
Mail.read_from_string(data.attr['BODY[]']) |
541 |
+ else |
|
542 |
+ Mail.read_from_string('') |
|
539 | 543 |
end |
540 | 544 |
end |
541 | 545 |
|
@@ -5,6 +5,12 @@ |
||
5 | 5 |
</li> |
6 | 6 |
<% end %> |
7 | 7 |
|
8 |
+ <% if agent.can_dry_run? %> |
|
9 |
+ <li> |
|
10 |
+ <%= link_to icon_tag('glyphicon-refresh') + ' Dry Run', '#', 'data-action-url' => dry_run_agent_path(agent), tabindex: "-1", onclick: "Utils.handleDryRunButton(this, '_method=PUT')" %> |
|
11 |
+ </li> |
|
12 |
+ <% end %> |
|
13 |
+ |
|
8 | 14 |
<li> |
9 | 15 |
<%= link_to icon_tag('glyphicon-eye-open') + ' Show'.html_safe, agent_path(agent) %> |
10 | 16 |
</li> |
@@ -9,7 +9,14 @@ |
||
9 | 9 |
<h2>Agent Event Flow</h2> |
10 | 10 |
</div> |
11 | 11 |
<div class="btn-group"> |
12 |
- <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, (params[:scenario_id] ? scenario_path(params[:scenario_id]) : agents_path), class: "btn btn-default" %> |
|
12 |
+ <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, (@scenario ? scenario_path(@scenario) : agents_path), class: "btn btn-default" %> |
|
13 |
+ <% if (num_disabled = @disabled_agents.count).nonzero? -%> |
|
14 |
+ <% if params[:exclude_disabled] %> |
|
15 |
+ <%= link_to @scenario ? scenario_diagram_path(@scenario) : diagram_path, class: 'btn btn-default' do %><%= icon_tag('glyphicon-eye-open') %> Show <%= pluralize(num_disabled, 'disabled Agent') %><% end %> |
|
16 |
+ <% else %> |
|
17 |
+ <%= link_to @scenario ? scenario_diagram_path(@scenario, exclude_disabled: true) : diagram_path(exclude_disabled: true), class: 'btn btn-default' do %><%= icon_tag('glyphicon-eye-close') %> Hide <%= pluralize(num_disabled, 'disabled Agent') %><% end %> |
|
18 |
+ <% end %> |
|
19 |
+ <% end %> |
|
13 | 20 |
</div> |
14 | 21 |
|
15 | 22 |
<div class='digraph'> |
@@ -2,6 +2,8 @@ require 'thread' |
||
2 | 2 |
require 'huginn_scheduler' |
3 | 3 |
require 'twitter_stream' |
4 | 4 |
|
5 |
+Rails.configuration.cache_classes = true |
|
6 |
+ |
|
5 | 7 |
STDOUT.sync = true |
6 | 8 |
STDERR.sync = true |
7 | 9 |
|
@@ -4,10 +4,10 @@ require 'rails/all' |
||
4 | 4 |
|
5 | 5 |
Bundler.require(:default, Rails.env) |
6 | 6 |
|
7 |
-Dotenv.overload File.expand_path('../../spec/env.test', __FILE__) if Rails.env.test? |
|
8 |
- |
|
9 | 7 |
module Huginn |
10 | 8 |
class Application < Rails::Application |
9 |
+ Dotenv.overload File.expand_path('../../spec/env.test', __FILE__) if Rails.env.test? |
|
10 |
+ |
|
11 | 11 |
# Settings in config/environments/* take precedence over those specified here. |
12 | 12 |
# Application configuration should go into files in config/initializers |
13 | 13 |
# -- all .rb files in that directory are automatically loaded. |
@@ -1,9 +1,10 @@ |
||
1 | 1 |
Delayed::Worker.destroy_failed_jobs = false |
2 | 2 |
Delayed::Worker.max_attempts = 5 |
3 |
-Delayed::Worker.max_run_time = 20.minutes |
|
3 |
+Delayed::Worker.max_run_time = (ENV['DELAYED_JOB_MAX_RUNTIME'].presence || 20).to_i.minutes |
|
4 | 4 |
Delayed::Worker.read_ahead = 5 |
5 | 5 |
Delayed::Worker.default_priority = 10 |
6 | 6 |
Delayed::Worker.delay_jobs = !Rails.env.test? |
7 |
+Delayed::Worker.sleep_delay = (ENV['DELAYED_JOB_SLEEP_DELAY'].presence || 10).to_f |
|
7 | 8 |
|
8 | 9 |
# Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log')) |
9 | 10 |
# Delayed::Worker.logger.level = Logger::DEBUG |
@@ -8,10 +8,10 @@ describe DryRunnable do |
||
8 | 8 |
|
9 | 9 |
def check |
10 | 10 |
log "Logging" |
11 |
- create_event payload: { test: "foo" } |
|
11 |
+ create_event payload: { 'test' => 'foo' } |
|
12 | 12 |
error "Recording error" |
13 |
- create_event payload: { test: "bar" } |
|
14 |
- self.memory = { last_status: "ok" } |
|
13 |
+ create_event payload: { 'test' => 'bar' } |
|
14 |
+ self.memory = { 'last_status' => 'ok', 'dry_run' => dry_run? } |
|
15 | 15 |
save! |
16 | 16 |
end |
17 | 17 |
end |
@@ -24,18 +24,41 @@ describe DryRunnable do |
||
24 | 24 |
} |
25 | 25 |
end |
26 | 26 |
|
27 |
- it "traps logging, event emission and memory updating" do |
|
27 |
+ def counts |
|
28 |
+ [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count] |
|
29 |
+ end |
|
30 |
+ |
|
31 |
+ it "does not affect normal run, with dry_run? returning false" do |
|
32 |
+ before = counts |
|
33 |
+ after = before.zip([0, 2, 2]).map { |x, d| x + d } |
|
34 |
+ |
|
35 |
+ expect { |
|
36 |
+ @agent.check |
|
37 |
+ @agent.reload |
|
38 |
+ }.to change { counts }.from(before).to(after) |
|
39 |
+ |
|
40 |
+ expect(@agent.memory).to eq({ 'last_status' => 'ok', 'dry_run' => false }) |
|
41 |
+ |
|
42 |
+ payloads = @agent.events.reorder(:id).last(2).map(&:payload) |
|
43 |
+ expect(payloads).to eq([{ 'test' => 'foo' }, { 'test' => 'bar' }]) |
|
44 |
+ |
|
45 |
+ messages = @agent.logs.reorder(:id).last(2).map(&:message) |
|
46 |
+ expect(messages).to eq(['Logging', 'Recording error']) |
|
47 |
+ end |
|
48 |
+ |
|
49 |
+ it "traps logging, event emission and memory updating, with dry_run? returning true" do |
|
28 | 50 |
results = nil |
29 | 51 |
|
30 | 52 |
expect { |
31 | 53 |
results = @agent.dry_run! |
54 |
+ @agent.reload |
|
32 | 55 |
}.not_to change { |
33 |
- [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count] |
|
56 |
+ [@agent.memory, counts] |
|
34 | 57 |
} |
35 | 58 |
|
36 | 59 |
expect(results[:log]).to match(/\AI, .+ INFO -- : Logging\nE, .+ ERROR -- : Recording error\n/) |
37 |
- expect(results[:events]).to eq([{ test: 'foo' }, { test: 'bar' }]) |
|
38 |
- expect(results[:memory]).to eq({ "last_status" => "ok" }) |
|
60 |
+ expect(results[:events]).to eq([{ 'test' => 'foo' }, { 'test' => 'bar' }]) |
|
61 |
+ expect(results[:memory]).to eq({ 'last_status' => 'ok', 'dry_run' => true }) |
|
39 | 62 |
end |
40 | 63 |
|
41 | 64 |
it "does not perform dry-run if Agent does not support dry-run" do |
@@ -45,8 +68,9 @@ describe DryRunnable do |
||
45 | 68 |
|
46 | 69 |
expect { |
47 | 70 |
results = @agent.dry_run! |
71 |
+ @agent.reload |
|
48 | 72 |
}.not_to change { |
49 |
- [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count] |
|
73 |
+ [@agent.memory, counts] |
|
50 | 74 |
} |
51 | 75 |
|
52 | 76 |
expect(results[:log]).to match(/\AE, .+ ERROR -- : Exception during dry-run. SandboxedAgent does not support dry-run: /) |